package com.zyf.project.binaryproto.contract.impl;

import com.zyf.project.binaryproto.contract.DataContractEncoder;
import com.zyf.project.binaryproto.contract.DataContractProxy;
import com.zyf.project.binaryproto.contract.DataTypeMapping;
import com.zyf.project.binaryproto.contract.FieldAttributeMapping;
import com.zyf.project.binaryproto.encoder.FieldEncoder;
import com.zyf.project.binaryproto.encoder.SliceEncoder;
import com.zyf.project.binaryproto.encoder.impl.HeaderEncoder;
import com.zyf.project.binaryproto.specification.DataSpecification;
import org.jetbrains.annotations.NotNull;
import utils.binary.impl.BytesSlice;
import utils.binary.io.BytesInputStream;
import utils.binary.io.BytesOutputBuffer;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @author Zhou Yifan
 */
public class DataContractEncoderImpl implements DataContractEncoder, DataTypeMapping {

    /**
     * 数据契约代理
     */
    private Class<?>[] contractProxyTypes;

    /**
     * 数据契约
     */
    private Class<?> contractType;

    /**
     * 数据契约格式标准
     */
    private DataSpecification specification;

    /**
     * 头部编码器
     */
    private HeaderEncoder headEncoder;

    /**
     * 字段编码器
     */
    private FieldEncoder[] fieldEncoders;

    /**
     * 字段的 Get 方法与编码器的映射表
     */
    private Map<Method, Integer> fieldIndexMap;

    /**
     * 构造函数列表
     */
    private Function<Integer, ?> arrayConstructor;

    /**
     * @param contractType  数据契约类型
     * @param specification 数据契约格式标准
     * @param headEncoder   头部编码器
     * @param fieldEncoders 按顺序排列的字段编码器列表
     */
    public DataContractEncoderImpl(Class<?> contractType, DataSpecification specification, HeaderEncoder headEncoder,
                                   FieldEncoder[] fieldEncoders) {
        this(contractType, specification, headEncoder, fieldEncoders, null);
    }

    /**
     * @param contractType     数据契约类型
     * @param specification    数据契约格式标准
     * @param headEncoder      头部编码器
     * @param fieldEncoders    按顺序排列的字段编码器列表
     * @param arrayConstructor 数组构造器
     */
    public DataContractEncoderImpl(Class<?> contractType, DataSpecification specification, HeaderEncoder headEncoder,
                                   FieldEncoder @NotNull [] fieldEncoders, Function<Integer, ?> arrayConstructor) {
        this.contractType = contractType;
        this.contractProxyTypes = new Class<?>[]{contractType, DataContractProxy.class};
        this.specification = specification;
        this.headEncoder = headEncoder;
        this.fieldEncoders = fieldEncoders;
        this.fieldIndexMap = new HashMap<>();
        int i = 0;
        for (FieldEncoder fieldEncoder : fieldEncoders) {
            fieldIndexMap.put(fieldEncoder.getReader(), i);
            i++;
        }
        this.arrayConstructor = arrayConstructor;
    }

    HeaderEncoder getHeaderEncoder() {
        return headEncoder;
    }

    /**
     * 数据契约动态代理对象要实现的接口类型；
     *
     * @return {@link Class}
     */
    Class<?>[] getContractProxyTypes() {
        return contractProxyTypes;
    }

    int getFieldCount() {
        return fieldEncoders.length;
    }

    FieldEncoder getFieldEncoder(int id) {
        return fieldEncoders[id];
    }

    /**
     * 通过字段的声明方法返回字段的序号；
     *
     * @param declaredMethod 声明并标注为数据契约字段的方法；注：不能是覆盖的非标注方法，也不能是实现方法；
     * @return 字段序号； 如果不存在，则返回 -1；
     */
    int getFieldId(Method declaredMethod) {
        Integer id = fieldIndexMap.get(declaredMethod);
        return id == null ? -1 : id;
    }

    /**
     * 通过字段的声明方法返回字段的编码器；
     *
     * @param declaredMethod 声明并标注为数据契约字段的方法；注：不能是覆盖的非标注方法，也不能是实现方法；
     * @return {@link FieldEncoder}
     */
    FieldEncoder getFieldEncoder(Method declaredMethod) {
        Integer idx = fieldIndexMap.get(declaredMethod);
        if (idx == null) {
            return null;
        }
        return fieldEncoders[idx];
    }

    /**
     * 获取数据契约的格式标准
     *
     * @return {@link DataSpecification}
     */
    @Override
    public DataSpecification getSpecification() {
        return specification;
    }

    /**
     * 获取数据契约的接口类型
     *
     * @return Class
     */
    @Override
    public Class<?> getContractType() {
        return contractType;
    }

    /**
     * 字段映射；
     *
     * @return {@link FieldAttributeMapping} 的 {@link List}
     */
    @Override
    public List<FieldAttributeMapping> getFieldMappings() {
        // TODO: Not implemented!;
        throw new IllegalStateException("Not implemented!");
    }

    /**
     * 将数据格式标准序列化，送到指定的 BytesOutputBuffer 中
     *
     * @param dataContract 数据
     * @param buffer       {@link BytesOutputBuffer}
     * @return 写入的字节数
     */
    @Override
    public int encode(Object dataContract, BytesOutputBuffer buffer) {
        if (dataContract instanceof DataContractProxy) {
            // 优化对数据动态代理对象的序列化，直接复制输出基础数组；
            DataContractProxy proxy = (DataContractProxy) dataContract;
            if (contractType == proxy.getContractType()) {
                byte[] dataBytes = new byte[proxy.getTotalSize()];
                proxy.writeBytes(dataBytes, 0);
                buffer.write(dataBytes);
                return dataBytes.length;
            }
        }

        int size = 0;
        size += headEncoder.encode(dataContract, buffer);
        if (dataContract != null) {
            for (SliceEncoder sliceEncoder : fieldEncoders) {
                size += sliceEncoder.encode(dataContract, buffer);
            }
        }
        return size;
    }

    /**
     * 按照数据格式标准将指定的二进制输入流反序列化生成数据对象；
     *
     * @param bytesStream {@link BytesInputStream}
     * @return 反序列化后对应的数据类型
     */
    @Override
    public <T> T decode(@NotNull BytesInputStream bytesStream) {
        BytesSlice headerSlice = bytesStream.getSlice(HeaderEncoder.HEAD_BYTES);

        if (!headEncoder.verifyContractCode(headerSlice)) {
            throw new IllegalArgumentException(String.format(
                    "The code and version resolved from bytes stream is not match this data contract encoder! --[expected=%s, %s][actual=%s, %s].",
                    headEncoder.getCode(), headEncoder.getVersion(), HeaderEncoder.resolveCode(headerSlice),
                    HeaderEncoder.resolveVersion(headerSlice)));
        }
        if (bytesStream.getSize() == HeaderEncoder.HEAD_BYTES) {
            // 只有头部，没有值，表示空值；
            return null;
        }
        return (T) DynamicDataContract.createContract(bytesStream, this);
    }

    /**
     * 数组创建函数，用于decode
     *
     * @return {@link Function}
     */
    @Override
    public Function<Integer, ?> getArrayConstructor() {
        return arrayConstructor;
    }
}
